#include "process_list.h"

/**
 * Initializes a new process list
 * @return NULL list element pointer
 */
PROCESS *procListCreate() {
    return NULL;
}

/**
 * Checks if it's a empty list
 * @param processList pointer of the processes list to be tested
 * @return true if it is or false if it isn't
 */
bool procListEmpty(PROCESS *processList) {
    if (processList == NULL) {
        return true;
    } else {
        return false;
    }
}

/**
 * Inserts a new process in a list of increasing processId processes
 * @param processList pointer of the processes list pointer
 * @param processId number of the process identifier
 * @param processSize number of pages of the process
 * @return true if the element linked or false if no memory allocated or processId already exists
 */
bool procListInsert(PROCESS **processList, int processId, int processSize) {
    PROCESS *newProcess = (PROCESS*) malloc(sizeof (PROCESS));

    if (newProcess != NULL) {
        // Got the process list malloc right
        newProcess->processId = processId;
        newProcess->processSize = processSize;
        newProcess->pageTable = (PAGE*) malloc(processSize * sizeof (PAGE));
        newProcess->next = NULL;

        if (newProcess->pageTable != NULL) {
            // Got the page table malloc right
            // Update the page table
            pagTabCreate(newProcess->pageTable, newProcess->processSize);

            PROCESS *iterator;
            PROCESS *previous;
            iterator = *processList;
            previous = NULL;

            // Get the right increasing processId position
            while (!procListEmpty(iterator) && iterator->processId < newProcess->processId) {
                previous = iterator;
                iterator = iterator->next;
            }

            // Check if the processId already exists
            if (!procListEmpty(iterator) && iterator->processId == newProcess->processId) {
                printf("ERROR procListInsert: This processId already exists\n");
                return false;
            } else {
                // It's a new processId
                if (procListEmpty(previous)) {
                    // Will be the first element
                    newProcess->next = iterator;
                    *processList = newProcess;
                    printf("PROCLIST procListInsert: inserted PID %d with %d pages\n", newProcess->processId, newProcess->processSize);
                    return true;
                } else {
                    previous->next = newProcess;
                    newProcess->next = iterator;
                    printf("PROCLIST procListInsert: inserted PID %d with %d pages\n", newProcess->processId, newProcess->processSize);
                    return true;
                }
            }
        } else {
            // No memory for the page table
            printf("ERROR procListInsert: No memory for the page table\n");
            return false;
        }
    } else {
        // No memory for the process
        printf("ERROR procListInsert: No memory for the process\n");
        return false;
    }
}

/**
 * Inserts a new process in a list of increasing processId processes
 * @param processList pointer of the processes list pointer
 * @param endedProcess to be linked in the list
 * @return true if the element linked or false if the process already exists
 */
bool endProcListInsert(PROCESS **processList, PROCESS *endedProcess) {
    PROCESS *iterator;
    PROCESS *previous;
    iterator = *processList;
    previous = NULL;

    // Get the right increasing processId position
    while (!procListEmpty(iterator) && iterator->processId < endedProcess->processId) {
        previous = iterator;
        iterator = iterator->next;
    }

    // Check if the processId already exists
    if (!procListEmpty(iterator) && iterator->processId == endedProcess->processId) {
        printf("ERROR procListInsert: This processId already exists\n");
        return false;
    } else {
        // It's a new processId
        if (procListEmpty(previous)) {
            // Will be the first element
            endedProcess->next = iterator;
            *processList = endedProcess;
            return true;
        } else {
            previous->next = endedProcess;
            endedProcess->next = iterator;
            return true;
        }
    }
}

/**
 * Prints status of process in a list
 * @param processList pointer of the processes list to be printed
 */
void procListPrint(PROCESS *processList) {
    PROCESS *iterator;
    iterator = processList;

    // Print all elements from the start
    while (!procListEmpty(iterator)) {
        printf("\nPROCESS: %d\n", iterator->processId);
        pagTabPrint(iterator->pageTable, iterator->processSize);
        iterator = iterator->next;
    }
}

/**
 * Find a process page in a processes list
 * @param processList list of avaliable processes
 * @param processId requested page process identification
 * @param pageNumber of the requested page
 * @return poiter to the requested page or NULL if invalid
 */
PAGE *pageValid(PROCESS *processList, int processId, int pageNumber) {
    PROCESS *iterator;
    iterator = processList;

    // Find the process in the list
    while (!procListEmpty(iterator) && iterator->processId != processId) {
        iterator = iterator->next;
    }

    // Got the process?
    if (!procListEmpty(iterator)) {
        // How about the page?
        if (iterator->processSize >= pageNumber) {
            // The page is 'real'
            PAGE *page;
            // Get a pointer to that page and return it
            page = iterator->pageTable + (pageNumber - 1);
            printf("PROCLIST pageValid: page %d\n", page->number);
            return page;
        } else {
            printf("ERROR pageValid: invalid page\n");
            return NULL;
        }
    } else {
        printf("ERROR pageValid: invalid process %d\n", processId);
        return NULL;
    }
}

/**
 * Remove a process from a process list
 * @param processList pointer of a process list start pointer
 * @param processId of the process to be removed
 * @return removed process pointer or NULL if no process to be removed
 */
PROCESS *procListRemove(PROCESS **processList, int processId) {
    PROCESS *iterator;
    PROCESS *previous;
    iterator = *processList;
    previous = NULL;

    // Find the process
    while (!procListEmpty(iterator) && iterator->processId != processId) {
        previous = iterator;
        iterator = iterator->next;
    }

    // Process exists?
    if (procListEmpty(iterator)) {
        printf("ERROR procListRemove: No process with that id\n");
        return NULL;
    } else {
        if (procListEmpty(previous)) {
            // It's the first process
            *processList = iterator->next;
            iterator->next = NULL;
            return iterator;
        } else {
            previous->next = iterator->next;
            iterator->next = NULL;
            return iterator;
        }
    }
}
